/// sv-parser adapter with error tolerance
/// Provides robust parsing with automatic fallback mechanisms
use crate::proto::*;
use anyhow::{anyhow, Result};
use std::collections::HashMap;
use std::path::PathBuf;
use sv_parser::{parse_sv, unwrap_node, Define, DefineText, Locate, RefNode};

/// Statistics for parsing operations
#[derive(Debug, Default, Clone)]
pub struct ParseStats {
    pub files_attempted: usize,
    pub files_succeeded: usize,
    pub modules_parsed: usize,
    pub modules_failed: usize,
    pub parser_mode: String,
}

/// sv-parser based Verilog parser with error tolerance
/// Currently a placeholder that will be fully implemented when sv-parser API is stable
pub struct SvParserAdapter {
    /// Parsed modules mapped by name
    modules: HashMap<String, Module>,
    /// Macro definitions
    macros: Vec<MacroDefine>,
    /// Predefined macro names
    predefined_macros: Vec<String>,
    /// Parse statistics
    stats: ParseStats,
}

impl SvParserAdapter {
    /// Create new adapter with predefined common macros
    pub fn new() -> Self {
        let mut adapter = Self {
            modules: HashMap::new(),
            macros: Vec::new(),
            predefined_macros: Vec::new(),
            stats: ParseStats::default(),
        };
        
        // Predefined common Verilog macros to improve robustness
        adapter.add_define_switch("PROTO_FPGA");
        adapter.add_define_switch("PRO_EMU");
        adapter.add_define_switch("SIMULATION");
        adapter.add_define_switch("SYNTHESIS");
        adapter.add_define_switch("FPGA_SYNTH");
        adapter.add_define_switch("ASIC_SYNTH");
        
        adapter
    }

    /// Add a switch macro (no value)
    pub fn add_define_switch(&mut self, name: &str) {
        self.predefined_macros.push(name.to_string());
        self.macros.push(MacroDefine {
            name: name.to_string(),
            value: String::new(),
            r#type: 0, // Switch macro
        });
    }

    /// Add a value macro
    pub fn add_define_value(&mut self, name: &str, value: &str) {
        self.predefined_macros.push(name.to_string());
        
        let macro_type = if value.contains('(') { 2 } else { 1 };
        self.macros.push(MacroDefine {
            name: name.to_string(),
            value: value.to_string(),
            r#type: macro_type,
        });
    }

    /// Parse file with error tolerance (main entry point)
    pub fn parse_file(&mut self, file_path: &str) -> Result<()> {
        self.stats.files_attempted += 1;
        
        // Try strict mode first
        match self.parse_file_strict(file_path) {
            Ok(_) => {
                self.stats.files_succeeded += 1;
                self.stats.parser_mode = "sv-parser-strict".to_string();
                Ok(())
            }
            Err(e) => {
                eprintln!("Warning: sv-parser strict mode failed on {}: {}", file_path, e);
                eprintln!("  Attempting tolerant mode...");
                
                // Try tolerant mode
                match self.parse_file_tolerant(file_path) {
                    Ok(_) => {
                        self.stats.files_succeeded += 1;
                        self.stats.parser_mode = "sv-parser-tolerant".to_string();
                        eprintln!("  → Tolerant mode succeeded");
                        Ok(())
                    }
                    Err(e2) => {
                        eprintln!("  → Tolerant mode also failed: {}", e2);
                        Err(anyhow!("Both parsing modes failed for {}", file_path))
                    }
                }
            }
        }
    }
    
    /// Strict parsing mode
    fn parse_file_strict(&mut self, file_path: &str) -> Result<()> {
        let path = PathBuf::from(file_path);
        if !path.exists() {
            return Err(anyhow!("File not found: {}", file_path));
        }

        // Build defines for sv-parser
        let defines = self.build_defines_map();
        let includes: Vec<PathBuf> = Vec::new();
        
        // Parse with sv-parser (strict: don't ignore includes/ifdefs)
        let (syntax_tree, _) = parse_sv(
            &path,
            &defines,
            &includes,
            false, // ignore_include
            false, // ignore_ifdef
        ).map_err(|e| anyhow!("sv-parser error: {:?}", e))?;

        self.extract_modules_from_tree(&syntax_tree, file_path)?;
        Ok(())
    }

    /// Tolerant parsing mode (relaxed settings)
    fn parse_file_tolerant(&mut self, file_path: &str) -> Result<()> {
        let path = PathBuf::from(file_path);
        let defines = self.build_defines_map();
        let includes: Vec<PathBuf> = Vec::new();
        
        // Parse with relaxed settings
        let (syntax_tree, _) = parse_sv(
            &path,
            &defines,
            &includes,
            true,  // ignore_include - skip missing files
            true,  // ignore_ifdef - treat undefined macros as false
        ).map_err(|e| anyhow!("sv-parser tolerant error: {:?}", e))?;

        self.extract_modules_from_tree(&syntax_tree, file_path)?;
        Ok(())
    }
    
    /// Build defines map for sv-parser (Define type)
    fn build_defines_map(&self) -> HashMap<String, Option<Define>> {
        self.predefined_macros.iter().map(|name| {
            // Find the macro value if it exists
            let define = self.macros.iter()
                .find(|m| m.name == *name)
                .map(|m| {
                    let text = if m.value.is_empty() {
                        None
                    } else {
                        Some(DefineText::new(m.value.clone(), None))
                    };
                    Define::new(name.clone(), vec![], text)
                });
            (name.clone(), define)
        }).collect()
    }
    
    /// Extract modules from syntax tree with per-module error handling
    fn extract_modules_from_tree(
        &mut self,
        syntax_tree: &sv_parser::SyntaxTree,
        source_file: &str,
    ) -> Result<()> {
        let mut success = 0;
        let mut failed = 0;

        // Iterate through all nodes to find module declarations
        for node in syntax_tree {
            match node {
                RefNode::ModuleDeclarationNonansi(x) => {
                    match self.extract_module_nonansi(x, syntax_tree, source_file) {
                        Ok(module) => {
                            self.modules.insert(module.name.clone(), module);
                            success += 1;
                        }
                        Err(e) => {
                            eprintln!("    Warning: Failed to parse non-ANSI module: {}", e);
                            failed += 1;
                        }
                    }
                }
                RefNode::ModuleDeclarationAnsi(x) => {
                    match self.extract_module_ansi(x, syntax_tree, source_file) {
                        Ok(module) => {
                            self.modules.insert(module.name.clone(), module);
                            success += 1;
                        }
                        Err(e) => {
                            eprintln!("    Warning: Failed to parse ANSI module: {}", e);
                            failed += 1;
                        }
                    }
                }
                _ => {}
            }
        }

        if success > 0 {
            eprintln!("  → Extracted {} modules ({} failed)", success, failed);
        }

        self.stats.modules_parsed += success;
        self.stats.modules_failed += failed;

        if success == 0 && failed > 0 {
            Err(anyhow!("No modules successfully parsed"))
        } else {
            Ok(())
        }
    }
    
    /// Extract non-ANSI module
    fn extract_module_nonansi(
        &self,
        node: &sv_parser::ModuleDeclarationNonansi,
        syntax_tree: &sv_parser::SyntaxTree,
        _source_file: &str,
    ) -> Result<Module> {
        // Get module identifier
        let id = unwrap_node!(node, ModuleIdentifier)
            .ok_or_else(|| anyhow!("No module identifier"))?;
        let module_name = self.get_identifier_string(id, syntax_tree)?;
        
        // Create module with basic info
        let mut module = Module {
            name: module_name.clone(),
            parameters: Vec::new(),
            ports: Vec::new(),
            wires: Vec::new(),
            instances: Vec::new(),
            info: format!("Parsed by sv-parser from non-ANSI declaration"),
            max_port_name_len: 0,
            max_port_width_len: 0,
            max_param_name_len: 0,
            max_param_value_len: 0,
            max_wire_name_len: 0,
        };
        
        // Parse module items (ports, wires, instances)
        self.parse_module_items(node, syntax_tree, &mut module)?;
        
        Ok(module)
    }

    /// Extract ANSI module
    fn extract_module_ansi(
        &self,
        node: &sv_parser::ModuleDeclarationAnsi,
        syntax_tree: &sv_parser::SyntaxTree,
        _source_file: &str,
    ) -> Result<Module> {
        // Get module identifier
        let id = unwrap_node!(node, ModuleIdentifier)
            .ok_or_else(|| anyhow!("No module identifier"))?;
        let module_name = self.get_identifier_string(id, syntax_tree)?;
        
        // Create module
        let mut module = Module {
            name: module_name.clone(),
            parameters: Vec::new(),
            ports: Vec::new(),
            wires: Vec::new(),
            instances: Vec::new(),
            info: format!("Parsed by sv-parser from ANSI declaration"),
            max_port_name_len: 0,
            max_port_width_len: 0,
            max_param_name_len: 0,
            max_param_value_len: 0,
            max_wire_name_len: 0,
        };
        
        // Parse ANSI port declarations
        self.parse_ansi_ports(node, syntax_tree, &mut module)?;
        
        // Parse module items
        self.parse_module_items(node, syntax_tree, &mut module)?;
        
        Ok(module)
    }
    
    /// Get identifier string from RefNode
    fn get_identifier_string(
        &self,
        node: RefNode,
        syntax_tree: &sv_parser::SyntaxTree,
    ) -> Result<String> {
        match unwrap_node!(node, SimpleIdentifier, EscapedIdentifier) {
            Some(RefNode::SimpleIdentifier(x)) => {
                Ok(syntax_tree.get_str(&x.nodes.0).unwrap_or("").to_string())
            }
            Some(RefNode::EscapedIdentifier(x)) => {
                Ok(syntax_tree.get_str(&x.nodes.0).unwrap_or("").to_string())
            }
            _ => Err(anyhow!("Could not extract identifier")),
        }
    }
    
    /// Parse ANSI port declarations
    fn parse_ansi_ports(
        &self,
        _node: &sv_parser::ModuleDeclarationAnsi,
        _syntax_tree: &sv_parser::SyntaxTree,
        _module: &mut Module,
    ) -> Result<()> {
        // ANSI port parsing would go here
        // For now, return Ok - ports will be empty but module structure is captured
        Ok(())
    }
    
    /// Parse module items (instances, wires, parameters)
    fn parse_module_items<T>(
        &self,
        _node: &T,
        _syntax_tree: &sv_parser::SyntaxTree,
        _module: &mut Module,
    ) -> Result<()> {
        // Module item parsing would go here
        // For now, return Ok - instances will be empty but module structure is captured
        Ok(())
    }

    /// Get parsing statistics
    pub fn get_stats(&self) -> &ParseStats {
        &self.stats
    }

    /// Get all parsed modules
    pub fn get_modules(&self) -> Vec<Module> {
        self.modules.values().cloned().collect()
    }

    /// Get all macros
    pub fn get_macros(&self) -> Vec<MacroDefine> {
        self.macros.clone()
    }

    /// Build complete database
    pub fn build_database(&self) -> ModbDatabase {
        ModbDatabase {
            modules: self.get_modules(),
            macros: self.get_macros(),
        }
    }
}

impl Default for SvParserAdapter {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_adapter_creation() {
        let adapter = SvParserAdapter::new();
        // Should have predefined macros
        assert!(!adapter.macros.is_empty());
        assert!(adapter.predefined_macros.contains(&"PROTO_FPGA".to_string()));
        assert!(adapter.predefined_macros.contains(&"SIMULATION".to_string()));
    }

    #[test]
    fn test_add_defines() {
        let mut adapter = SvParserAdapter::new();
        let initial_count = adapter.macros.len();
        
        adapter.add_define_switch("MY_MACRO");
        adapter.add_define_value("WIDTH", "32");
        
        assert_eq!(adapter.macros.len(), initial_count + 2);
        assert!(adapter.predefined_macros.contains(&"MY_MACRO".to_string()));
        assert!(adapter.predefined_macros.contains(&"WIDTH".to_string()));
    }
    
    #[test]
    fn test_fallback_behavior() {
        let mut adapter = SvParserAdapter::new();
        let result = adapter.parse_file("test.v");
        
        // Should fail gracefully and trigger fallback
        assert!(result.is_err());
        assert_eq!(adapter.stats.files_attempted, 1);
    }
}
